package com.easy.common.util.auth;
import com.easy.common.constant.SysProps;

public class PassEncrypt {
	private static final byte MD5_MAGIC[] = {
		36, 49, 36
	};
	@SuppressWarnings("unused")
	private static final int MD5_MAGIC_LEN = 3;
	@SuppressWarnings("unused")
	private static final int PHP_MAX_SALT_LEN = 12;
	@SuppressWarnings("unused")
	private static final int S11 = 7;
	@SuppressWarnings("unused")
	private static final int S12 = 12;
	@SuppressWarnings("unused")
	private static final int S13 = 17;
	@SuppressWarnings("unused")
	private static final int S14 = 22;
	@SuppressWarnings("unused")
	private static final int S21 = 5;
	@SuppressWarnings("unused")
	private static final int S22 = 9;
	@SuppressWarnings("unused")
	private static final int S23 = 14;
	@SuppressWarnings("unused")
	private static final int S24 = 20;
	@SuppressWarnings("unused")
	private static final int S31 = 4;
	@SuppressWarnings("unused")
	private static final int S32 = 11;
	@SuppressWarnings("unused")
	private static final int S33 = 16;
	@SuppressWarnings("unused")
	private static final int S34 = 23;
	@SuppressWarnings("unused")
	private static final int S41 = 6;
	@SuppressWarnings("unused")
	private static final int S42 = 10;
	@SuppressWarnings("unused")
	private static final int S43 = 15;
	@SuppressWarnings("unused")
	private static final int S44 = 21;
	
	private static byte itoa16[] = {
		48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 
		65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 
		75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 
		85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 
		101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 
		111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 
		121, 122
	};
	private static byte itoa64[] = {
		46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 
		56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 
		73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 
		83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 
		99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 
		109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 
		119, 120, 121, 122
	};
	private static byte PADDING[] = {
		-128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
		0, 0, 0, 0
	};

	public PassEncrypt()
	{
	}

	public static void outputLong(long src)
	{
		byte array[] = new byte[16];
		long src2 = src;
		for (int i = 0; i < 16; i++)
		{
			array[15 - i] = (byte)(int)(src2 & 15L);
			src2 >>>= 4;
		}

		for (int i = 0; i < 16; i++)
			System.out.print((char)itoa16[array[i]]);

	}

	public static void outputInt(int src)
	{
		byte array[] = new byte[8];
		int src2 = src;
		for (int i = 0; i < 8; i++)
		{
			array[7 - i] = (byte)(int)((long)src2 & 15L);
			src2 >>>= 4;
		}

		for (int i = 0; i < 8; i++)
			System.out.print((char)itoa16[array[i]]);

	}

	public static long int2Long(int src)
	{
		long rtValue = (long)src & 0xffffffffL;
		return rtValue;
	}

	public static int add(int i1, int i2)
	{
		long rtValue = int2Long(i1) + int2Long(i2);
		rtValue &= 0xffffffffL;
		return (int)rtValue;
	}

	public static int byte2Int(byte src)
	{
		int rtValue = src & 0xff;
		return rtValue;
	}

	private static int F(int x, int y, int z)
	{
		return x & y | ~x & z;
	}

	private static int G(int x, int y, int z)
	{
		return x & z | y & ~z;
	}

	private static int H(int x, int y, int z)
	{
		return x ^ y ^ z;
	}

	private static int I(int x, int y, int z)
	{
		return y ^ (x | ~z);
	}

	private static int ROTATE_LEFT(int x, int n)
	{
		return x << n | x >>> 32 - n;
	}

	private static int FF(int a, int b, int c, int d, int x, int s, int ac)
	{
		int a0 = add(add(F(b, c, d), x), ac);
		a = add(a, a0);
		a = ROTATE_LEFT(a, s);
		a = add(a, b);
		return a;
	}

	private static int GG(int a, int b, int c, int d, int x, int s, int ac)
	{
		int a0 = add(add(G(b, c, d), x), ac);
		a = add(a, a0);
		a = ROTATE_LEFT(a, s);
		a = add(a, b);
		return a;
	}

	private static int HH(int a, int b, int c, int d, int x, int s, int ac)
	{
		int a0 = add(add(H(b, c, d), x), ac);
		a = add(a, a0);
		a = ROTATE_LEFT(a, s);
		a = add(a, b);
		return a;
	}

	private static int II(int a, int b, int c, int d, int x, int s, int ac)
	{
		int a0 = add(add(I(b, c, d), x), ac);
		a = add(a, a0);
		a = ROTATE_LEFT(a, s);
		a = add(a, b);
		return a;
	}

	private static void Encode(byte output[], int input[], int len)
	{
		int i = 0;
		for (int j = 0; j < len; j += 4)
		{
			output[j] = (byte)(input[i] & 0xff);
			output[j + 1] = (byte)(input[i] >>> 8 & 0xff);
			output[j + 2] = (byte)(input[i] >>> 16 & 0xff);
			output[j + 3] = (byte)(input[i] >>> 24 & 0xff);
			i++;
		}

	}

	private static void Decode(int output[], byte input[], int len)
	{
		int i = 0;
		for (int j = 0; j < len; j += 4)
		{
			output[i] = byte2Int(input[j]) | byte2Int(input[j + 1]) << 8 | byte2Int(input[j + 2]) << 16 | byte2Int(input[j + 3]) << 24;
			i++;
		}

	}

	private static void PHP_MD5Init(MD5Context context)
	{
		context.count[0] = context.count[1] = 0;
		context.state[0] = 0x67452301;
		context.state[1] = 0xefcdab89;
		context.state[2] = 0x98badcfe;
		context.state[3] = 0x10325476;
	}

	private static void memcpy(byte dest[], byte src[], int offsetDest, int offesetSrc, int length)
	{
		for (int i = 0; i < length; i++)
			dest[offsetDest + i] = src[offesetSrc + i];

	}

	private static void PHP_MD5Update(MD5Context context, byte input[], int inputLen)
	{
		int index = context.count[0] >>> 3 & 0x3f;
		if (int2Long(context.count[0] = add(context.count[0], inputLen << 3)) < int2Long(inputLen << 3))
			context.count[1] = add(context.count[1], 1);
		context.count[1] = add(context.count[1], inputLen >>> 29);
		int partLen = 64 - index;
		int i;
		if (inputLen >= partLen)
		{
			memcpy(context.buffer, input, index, 0, partLen);
			MD5Transform(context.state, context.buffer);
			for (i = partLen; i + 63 < inputLen; i += 64)
			{
				byte buff[] = new byte[64];
				memset(buff, (byte)0);
				int len = input.length - i;
				memcpy(buff, input, 0, i, len >= 64 ? 64 : len);
				MD5Transform(context.state, buff);
			}

			index = 0;
		} else
		{
			i = 0;
		}
		memcpy(context.buffer, input, index, i, inputLen - i);
	}

	private static void memset(byte src[], byte value)
	{
		for (int i = 0; i < src.length; i++)
			src[i] = value;

	}

	private static void memset(int src[], int value)
	{
		for (int i = 0; i < src.length; i++)
			src[i] = value;

	}

	private static void MD5Transform(int state[], byte block[])
	{
		int a = state[0];
		int b = state[1];
		int c = state[2];
		int d = state[3];
		int x[] = new int[16];
		Decode(x, block, 64);
		a = FF(a, b, c, d, x[0], 7, 0xd76aa478);
		d = FF(d, a, b, c, x[1], 12, 0xe8c7b756);
		c = FF(c, d, a, b, x[2], 17, 0x242070db);
		b = FF(b, c, d, a, x[3], 22, 0xc1bdceee);
		a = FF(a, b, c, d, x[4], 7, 0xf57c0faf);
		d = FF(d, a, b, c, x[5], 12, 0x4787c62a);
		c = FF(c, d, a, b, x[6], 17, 0xa8304613);
		b = FF(b, c, d, a, x[7], 22, 0xfd469501);
		a = FF(a, b, c, d, x[8], 7, 0x698098d8);
		d = FF(d, a, b, c, x[9], 12, 0x8b44f7af);
		c = FF(c, d, a, b, x[10], 17, -42063);
		b = FF(b, c, d, a, x[11], 22, 0x895cd7be);
		a = FF(a, b, c, d, x[12], 7, 0x6b901122);
		d = FF(d, a, b, c, x[13], 12, 0xfd987193);
		c = FF(c, d, a, b, x[14], 17, 0xa679438e);
		b = FF(b, c, d, a, x[15], 22, 0x49b40821);
		a = GG(a, b, c, d, x[1], 5, 0xf61e2562);
		d = GG(d, a, b, c, x[6], 9, 0xc040b340);
		c = GG(c, d, a, b, x[11], 14, 0x265e5a51);
		b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);
		a = GG(a, b, c, d, x[5], 5, 0xd62f105d);
		d = GG(d, a, b, c, x[10], 9, 0x2441453);
		c = GG(c, d, a, b, x[15], 14, 0xd8a1e681);
		b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);
		a = GG(a, b, c, d, x[9], 5, 0x21e1cde6);
		d = GG(d, a, b, c, x[14], 9, 0xc33707d6);
		c = GG(c, d, a, b, x[3], 14, 0xf4d50d87);
		b = GG(b, c, d, a, x[8], 20, 0x455a14ed);
		a = GG(a, b, c, d, x[13], 5, 0xa9e3e905);
		d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8);
		c = GG(c, d, a, b, x[7], 14, 0x676f02d9);
		b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
		a = HH(a, b, c, d, x[5], 4, 0xfffa3942);
		d = HH(d, a, b, c, x[8], 11, 0x8771f681);
		c = HH(c, d, a, b, x[11], 16, 0x6d9d6122);
		b = HH(b, c, d, a, x[14], 23, 0xfde5380c);
		a = HH(a, b, c, d, x[1], 4, 0xa4beea44);
		d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9);
		c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60);
		b = HH(b, c, d, a, x[10], 23, 0xbebfbc70);
		a = HH(a, b, c, d, x[13], 4, 0x289b7ec6);
		d = HH(d, a, b, c, x[0], 11, 0xeaa127fa);
		c = HH(c, d, a, b, x[3], 16, 0xd4ef3085);
		b = HH(b, c, d, a, x[6], 23, 0x4881d05);
		a = HH(a, b, c, d, x[9], 4, 0xd9d4d039);
		d = HH(d, a, b, c, x[12], 11, 0xe6db99e5);
		c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
		b = HH(b, c, d, a, x[2], 23, 0xc4ac5665);
		a = II(a, b, c, d, x[0], 6, 0xf4292244);
		d = II(d, a, b, c, x[7], 10, 0x432aff97);
		c = II(c, d, a, b, x[14], 15, 0xab9423a7);
		b = II(b, c, d, a, x[5], 21, 0xfc93a039);
		a = II(a, b, c, d, x[12], 6, 0x655b59c3);
		d = II(d, a, b, c, x[3], 10, 0x8f0ccc92);
		c = II(c, d, a, b, x[10], 15, 0xffeff47d);
		b = II(b, c, d, a, x[1], 21, 0x85845dd1);
		a = II(a, b, c, d, x[8], 6, 0x6fa87e4f);
		d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
		c = II(c, d, a, b, x[6], 15, 0xa3014314);
		b = II(b, c, d, a, x[13], 21, 0x4e0811a1);
		a = II(a, b, c, d, x[4], 6, 0xf7537e82);
		d = II(d, a, b, c, x[11], 10, 0xbd3af235);
		c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb);
		b = II(b, c, d, a, x[9], 21, 0xeb86d391);
		state[0] = add(state[0], a);
		state[1] = add(state[1], b);
		state[2] = add(state[2], c);
		state[3] = add(state[3], d);
		memset(x, 0);
	}

	private static void PHP_MD5Final(byte digest[], MD5Context context)
	{
		byte bits[] = new byte[8];
		int index = 0;
		int padLen = 0;
		Encode(bits, context.count, 8);
		index = context.count[0] >>> 3 & 0x3f;
		padLen = index >= 56 ? 120 - index : 56 - index;
		PHP_MD5Update(context, PADDING, padLen);
		PHP_MD5Update(context, bits, 8);
		Encode(digest, context.state, 16);
		context.set0();
	}

	private static void to64(byte s[], int offset, int v, int n)
	{
		int i = 0;
		while (--n >= 0) 
		{
			s[offset + i++] = itoa64[v & 0x3f];
			v >>>= 6;
		}
	}

	private static int strncmp(byte str1[], byte str2[], int from1, int from2, int len)
	{
		for (int i = 0; i < len; i++)
		{
			if (str1[from1 + i] > str2[from2 + i])
				return 1;
			if (str1[from1 + i] < str2[from2 + i])
				return -1;
		}

		return 0;
	}

	private static byte[] cloneArray(byte src[], int offset, int len)
	{
		byte rtArray[] = new byte[len];
		for (int i = 0; i < len; i++)
			rtArray[i] = src[offset + i];

		return rtArray;
	}

	private static int strlcpy(byte dst[], byte src[], int siz, int dstOffset, int srcOffset)
	{
		int d = 0;
		int s = 0;
		int n = siz;
		if (n != 0 && --n != 0)
			while ((dst[dstOffset + d++] = src[srcOffset + s++]) != 0 && --n != 0) ;
		if (n == 0)
		{
			if (siz != 0)
				dst[dstOffset + d] = 0;
			for (; s < src.length && src[s] != 0; s++);
		}
		return s - 1;
	}

	private static byte[] md5_crypt(byte pw[], byte salt[])
	{
		byte passwd[] = new byte[120];
		int p = 0;
		int sp = 0;
		int ep = 0;
		byte finalArray[] = new byte[16];
		MD5Context ctx = new MD5Context();
		MD5Context ctx1 = new MD5Context();
		int pwl = pw.length;
		sp = 0;
		if (strncmp(salt, MD5_MAGIC, sp, 0, 3) == 0)
			sp += 3;
		for (ep = sp; salt[ep] != 0 && salt[ep] != 36 && ep < sp + 8; ep++);
		int sl = ep - sp;
		PHP_MD5Init(ctx);
		PHP_MD5Update(ctx, pw, pwl);
		PHP_MD5Update(ctx, MD5_MAGIC, 3);
		PHP_MD5Update(ctx, cloneArray(salt, sp, sl), sl);
		PHP_MD5Init(ctx1);
		PHP_MD5Update(ctx1, pw, pwl);
		PHP_MD5Update(ctx1, cloneArray(salt, sp, sl), sl);
		PHP_MD5Update(ctx1, pw, pwl);
		PHP_MD5Final(finalArray, ctx1);
		for (int pl = pwl; pl > 0; pl -= 16)
			PHP_MD5Update(ctx, finalArray, pl <= 16 ? pl : 16);

		memset(finalArray, (byte)0);
		for (int i = pwl; i != 0; i >>>= 1)
			if ((i & 1) != 0)
				PHP_MD5Update(ctx, finalArray, 1);
			else
				PHP_MD5Update(ctx, pw, 1);

		memcpy(passwd, MD5_MAGIC, 0, 0, 3);
		strlcpy(passwd, salt, sl + 1, 3, sp);
		passwd[3 + sl] = 36;
		PHP_MD5Final(finalArray, ctx);
		for (int i = 0; i < 1000; i++)
		{
			PHP_MD5Init(ctx1);
			if ((i & 1) != 0)
				PHP_MD5Update(ctx1, pw, pwl);
			else
				PHP_MD5Update(ctx1, finalArray, 16);
			if (i % 3 != 0)
				PHP_MD5Update(ctx1, cloneArray(salt, sp, sl), sl);
			if (i % 7 != 0)
				PHP_MD5Update(ctx1, pw, pwl);
			if ((i & 1) != 0)
				PHP_MD5Update(ctx1, finalArray, 16);
			else
				PHP_MD5Update(ctx1, pw, pwl);
			PHP_MD5Final(finalArray, ctx1);
		}

		p = sl + 3 + 1;
		int l = byte2Int(finalArray[0]) << 16 | byte2Int(finalArray[6]) << 8 | byte2Int(finalArray[12]);
		to64(passwd, p, l, 4);
		p += 4;
		l = byte2Int(finalArray[1]) << 16 | byte2Int(finalArray[7]) << 8 | byte2Int(finalArray[13]);
		to64(passwd, p, l, 4);
		p += 4;
		l = byte2Int(finalArray[2]) << 16 | byte2Int(finalArray[8]) << 8 | byte2Int(finalArray[14]);
		to64(passwd, p, l, 4);
		p += 4;
		l = byte2Int(finalArray[3]) << 16 | byte2Int(finalArray[9]) << 8 | byte2Int(finalArray[15]);
		to64(passwd, p, l, 4);
		p += 4;
		l = byte2Int(finalArray[4]) << 16 | byte2Int(finalArray[10]) << 8 | byte2Int(finalArray[5]);
		to64(passwd, p, l, 4);
		p += 4;
		l = byte2Int(finalArray[11]);
		to64(passwd, p, l, 2);
		p += 2;
		passwd[p] = 0;
		byte rtArray[] = new byte[p];
		memcpy(rtArray, passwd, 0, 0, p);
		return rtArray;
	}

	private static byte[] crypt(byte pw[], byte salt[])
	{
		if (salt.length > 3 && strncmp(salt, MD5_MAGIC, 0, 0, 3) == 0)
			return md5_crypt(pw, salt);
		else
			return null;
	}

	private static void php_to64(byte s[], int v, int n, int sOffset)
	{
		while (--n >= 0) 
		{
			s[sOffset++] = itoa64[v & 0x3f];
			v >>>= 6;
		}
	}

	private static byte[] crypt(byte pw[])
	{
		byte salt[] = new byte[12];
		salt[0] = 36;
		salt[1] = 49;
		salt[2] = 36;
		int rand = 0;
		rand = (int)(Math.random() * 1000000000D);
		php_to64(salt, rand, 4, 3);
		rand = (int)(Math.random() * 1000000000D);
		php_to64(salt, rand, 4, 7);
		salt[11] = 36;
		return crypt(pw, salt);
	}

	public static String encrypt(String srcStr, String salt)
	{
		char charPass[];
		try{
			byte cryptPass[] = crypt(srcStr.getBytes("UTF-8"), salt.getBytes("UTF-8"));
			int passLen = cryptPass.length;
			charPass = new char[passLen];
			for (int i = 0; i < passLen; i++)
				charPass[i] = (char)cryptPass[i];
	
			return new String(charPass);
		}
		catch(Exception ex){
			ex.printStackTrace();			
		}
		return "";
	}

	public static String encryptPass(String pass)
	{
		try{
			if (SysProps.getString("encryptPassMethod").equalsIgnoreCase("md5")){
				return DigestUtility.md5Hex(pass.getBytes("UTF-8"));
			}
		}
		catch(Exception ex){
			ex.printStackTrace();			
			return "";
		}
		byte passArray[] = UsbKey.str2Bytes(pass);
		byte cryptPass[] = crypt(passArray);
		int passLen = cryptPass.length;
		char charPass[] = new char[passLen];
		for (int i = 0; i < passLen; i++)
			charPass[i] = (char)cryptPass[i];

		return new String(charPass);
	}

	public static boolean isValidPas(String realPass, String encryptPass)
	{
		try{
			if(SysProps.getString("encryptPassMethod").equalsIgnoreCase("md5")){
				String tmpPass = DigestUtility.md5Hex(realPass.getBytes("UTF-8")).toUpperCase();
				return tmpPass.equalsIgnoreCase(encryptPass);
			}
		}
		catch(Exception ex){
			ex.printStackTrace();			
			return false;
		}
		int tmpInt = encryptPass.lastIndexOf("$");
		if (tmpInt < 0)return false;
		byte pw[] = UsbKey.str2Bytes(realPass);
		byte salt[] = UsbKey.str2Bytes(encryptPass.substring(0, tmpInt + 1));
		byte srcPassArray[] = UsbKey.str2Bytes(encryptPass);
		byte tmpPass[] = crypt(pw, salt);
		int len = tmpPass.length;
		if (len != srcPassArray.length)return false;
		for (int i = 0; i < len; i++){
			if (tmpPass[i] != srcPassArray[i])return false;
		}
		return true;
	}

}
